﻿/* WSDL Cache */
var cacheWsdl = new Array();
/* Load AUI Object */
var aui;
/* Array to store AUI objects */
var idx = new Array();
/* Array to store Data Objets */
var dataIdx = new Array();
/* Variables used for timing purposes */
var timeout;
var interval;
var mouseingOverOptions = false;

/* List of Unique Items visited */
var u_Items;
/*
_AUI Object: for passing 'Predictive Feature' data to the database.
*/
function _AUI(id, time, yM, yA, dTime, nrVI, uIC, sTime, avgDTime, KLMR, KLMD) {
    this.id = id;  // insert the name of the drop down list here so it can be identified "type:string"
    this.totalTime = parseInt(time);          //int
    this.yMouseV = parseInt(yM);              //int
    this.yMouseA = parseInt(yA);              //int
    this.dwellTime = parseInt(dTime);         //int
    this.nrVisitedItems = parseInt(nrVI);
    this.uniqueItemCount = parseInt(uIC);     //int
    this.selectionTime = parseInt(sTime);     //int
    this.avgDwellTime = parseInt(avgDTime);   //int
    this.KLMRatio = parseFloat(KLMR);         //double
    this.KLMDifference = parseFloat(KLMD);    //double
    this.KLMPredictedTime = 2.65;             //double
}
/*
Data Boject: stores the name of an input element and its value, and stores it in an array which is passed to the database.
*/
function DataObject(name, value) {
    this.Name = name;
    this.Value = value;
}
/* ************************************************************* */
/* ******************* Drop Down List ******************* */
/* ************************************************************* */
// Click on drop down list
$('.dropdownlist').livequery('mouseenter', function() {
    aui = new AUI();
    // Reset
    aui.reset();
    // Initialise();
    aui.initialise();
    // Begin capturing
    aui.start();
    // set aui object ID
    aui.setId($(this).attr("id"));
    //setuplist of uniqueitems
    u_Items = [];
    clearTimeout(timeout);
    clearInterval(interval);
    timeout = setTimeout('startIncrement()', 1000);
    return false;
});
// Move mouse over drop down list
$('.dropdownlist').livequery('mousemove', function(e) {
    clearTimeout(timeout);
    clearInterval(interval);
    timeout = setTimeout('startIncrement()', 1000);
});
// Move mouse out of drop down list
$('.dropdownlist').livequery('mouseleave', function(e) {
    // TODO: stall for 100 milliseconds then check if mouse moved over list. If yes = continue increasing ONLY dwell time and don't stop AUI, else reset everything.
    aui.stop();
    clearTimeout(timeout);
    clearInterval(interval);
    var item_height = $(this).children("option:selected").height();
    var items_visited = ((parseInt(aui.NrVisitedItems()) + 1) / 2) - 0.5;
    var distance = item_height * items_visited;
    aui.SetUniqueItemsCount(u_Items.length);
    aui.SetNumberOfVisitedItems(items_visited);
    aui.YMouseVelocity(distance, aui.SelectionTime());
    aui.YMouseAcceleration(distance, aui.SelectionTime());
    _aui = new _AUI(aui.getId(),
                    aui.TotalTime(),
                    aui.YMouseVelocity(distance, aui.SelectionTime()),
                    aui.YMouseAcceleration(distance, aui.SelectionTime()),
                    aui.DwellTime(),
                    aui.NrVisitedItems(),
                    aui.UniqueItemsVisited(),
                    aui.SelectionTime(),
                    aui.AverageDwellTime(aui.DwellTime(), items_visited),
                    aui.KLMRatio(aui.TotalTime()),
                    aui.KLMDifference(aui.TotalTime())
                    );
    addAUI(_aui, idx);
    //aui.reset();
    clear(u_Items);
});
$('.dropdownlist').livequery('change', function() {
    aui.stop();
    clearTimeout(timeout);
    clearInterval(interval);
    //test: computing distance - for yVelocity etc
    var item_height = $(this).children("option:selected").height();
    var items_visited = ((parseInt(aui.NrVisitedItems()) + 1) / 2) - 0.5;
    var distance = item_height * items_visited;
    aui.SetUniqueItemsCount(u_Items.length);
    aui.SetNumberOfVisitedItems(items_visited);
    aui.YMouseVelocity(distance, aui.SelectionTime());
    aui.YMouseAcceleration(distance, aui.SelectionTime());
    //NOTE: Once the alert is removed, replace 'aui.YMouseV" and 'aui.YMouseA' with the functions from above"
    _aui = new _AUI(aui.getId(),
                    aui.TotalTime(),
                    aui.YMouseVelocity(distance, aui.SelectionTime()),
                    aui.YMouseAcceleration(distance, aui.SelectionTime()),
                    aui.DwellTime(),
                    aui.NrVisitedItems(),
                    aui.UniqueItemsVisited(),
                    aui.SelectionTime(),
                    aui.AverageDwellTime(aui.DwellTime(), items_visited),
                    aui.KLMRatio(aui.TotalTime()),
                    aui.KLMDifference(aui.TotalTime())
                    );
    addAUI(_aui, idx);
    // reset global aui 
    aui.reset();
    clear(u_Items);
    //Value of selected item
    var selected = $(this).children("option:selected").val();
    return false;
});
/* ************************************************************* */
/* ******************* Drop Down List Option ******************* */
/* ************************************************************* */
// Mouse over drop down list options
$('.dropdownlist option').live('mouseover', function() {
    clearTimeout(timeout);
    clearInterval(interval);
    mouseingOverOptions = true;
    //Increment count of visited item
    aui.IncrVisited();
    // When mouseover, it adds 2(bug)
    console.log("Visited: " + ((parseInt(aui.NrVisitedItems()) + 1) / 2));
    //aui.AddSelectedToHash($(this).val());
    add(u_Items, $(this).val());
    timeout = setTimeout('startIncrement()', 1000);
})
$('.dropdownlist option').live('mousemove', function() {
    clearTimeout(timeout);
    clearInterval(interval);
    timeout = setTimeout('startIncrement()', 1000);
})
// Mouse moves off drop down list options stops timer
$('.dropdownlist option').live('mouseleave', function() {
    aui.stop();
    clearTimeout(timeout);
    clearInterval(interval);
    mouseingOverOptions = false;
    aui.reset();
    clear(u_Items);
    //console.log("mouseleave event for element: '" + $(this).val() + "' fired");
})

//WHAT is this?
$('.dropdownlist option').live('mouseover', function() {
    clearTimeout(timeout);
    clearInterval(interval);
    //Increment count of visited item
    aui.IncrVisited();
})
/* ************************************************************* */
/* ******************* TextBox ******************* */
/* ************************************************************* */
/* Return Key value = 13 i.e. 'e.which == 13 '*/
//  NOTE: Text box keyevents are generated with the WSDL xslt
/* ************************************************************* */
/* ******************* On Task Complete ******************* */
/* ************************************************************* */
// Given the current step, validate, and move to next step
function nextStep(curStep) {
    if (curStep !== 4) {
        showLoader();
        // Check that we can move to next step (validation etc): do this last - function "check_All_Steps" returns either 'success' or String with error message (Custom message, not the stacktrace or anything confusing). If NOT succes, make visible a panel that displays this error
        // post PF metric data to the database
        var pl = new SOAPClientParameters();
        pl.add("idx", idx);
        SOAPClient.invoke(url_watcher, "listMetrics", pl, true, doNothing);
        //reset the stored aui objects list for next step
        idx = []; //clear(idx);
        //reset timers (just in case theres a 'leak')
        clearTimeout(timeout);
        clearInterval(interval);

        // TODO:write task information to database
        var pa = new SOAPClientParameters();
        pa.add("step", curStep); // current step
        pa.add("idx", FillData(dataIdx));  // processing data
        SOAPClient.invoke(url_processing, "processStep", pa, true, clearData);

        // TODO:write task information to database
        pa.clear();
        pa.add("step", curStep); // current step
        SOAPClient.invoke(url_taskmodel, "StepComplete", pa, true, doNothing);

        // We haven't reached the end of the tasks yet so move to next task
        transform(curStep + 1, _stereotype);
        
        
    } else {
        alert('Task Complete');
        showLoader();
        // post PF metric data to the database
        var pl = new SOAPClientParameters();
        pl.add("idx", idx);
        SOAPClient.invoke(url_watcher, "listMetrics", pl, true, isExpert);
        //reset the stored aui objects list for new task
        idx = [];
        //house cleaning - just in case
        clearTimeout(timeout);
        clearInterval(interval);

        // TODO:write task information to database
        var pa = new SOAPClientParameters();
        pa.add("step", curStep);            // current step
        pa.add("idx", FillData(dataIdx));  // processing data
        SOAPClient.invoke(url_processing, "processStep", pa, true, function() {
            clear(dataIdx);
        });

        pa.clear();
        pa.add("totalTaskTime", totalTaskTime);
        SOAPClient.invoke(url_processing, "LogTaskTime", pa, true, function() {
            clear(dataIdx);
        });

        //check expertise
        pa.clear();
        pa.add("userid", _userid);
        SOAPClient.invoke(url_expertise, "isExpert", pa, false, function(r) {
            if (r === true) {
                alert("Your performance with the application has improved \n\n You will now be moved to an expert user interface");
                _stereotype = "Expert";
            }
        });       
        
        
        reserTaskTimer();
        
        clear(dataIdx);

        //generated code NOV.xslt: unbinds live query code for generated javascript. 
        expireLiveQueryEvents();
        
        //  end the task - restart
        transform(1, _stereotype);
    }
}

//Expert Step function
function TaskComplete() {
    showLoader();
    // Check that we can move to next step (validation etc): do this last - function "check_All_Steps" returns either 'success' or String with error message (Custom message, not the stacktrace or anything confusing). If NOT succes, make visible a panel that displays this error
    // post PF metric data to the database
    var pl = new SOAPClientParameters();
    pl.add("idx", idx);
    SOAPClient.invoke(url_watcher, "listMetrics", pl, false, doNothing);
    //reset the stored aui objects list for next step
    idx = []; //clear(idx);
    //reset timers (just in case theres a 'leak')
    clearTimeout(timeout);
    clearInterval(interval);

    // TODO:write task information to database
    var pa = new SOAPClientParameters();
    pa.add("step", 5); // current step
    pa.add("idx", FillData(dataIdx));  // processing data
    SOAPClient.invoke(url_processing, "processStep", pa, true, doNothing);
    

    pa.clear();
    pa.add("totalTaskTime", totalTaskTime);
    SOAPClient.invoke(url_processing, "LogTaskTime", pa, true, function() {
        clear(dataIdx);
    });
    
    clearPage();
    hideLoader();
    reserTaskTimer();
}

// function collects data from all UI elements and places in array
function FillData(list) {

    var selects = $('select');
    var inputs = $('input');
    var id, text;
    var dObject;

    //Select elements
    for (var i = 0; i < selects.length; i++) {
        id = selects[i].id;
        text = selects[i].value;
        dObject = new DataObject(id, text);

        list.unshift(dObject);
    }
    //Input elements (text fields etc)
    for (var i = 0; i < inputs.length; i++) {
        //check that the element is not hidden
        if ($(inputs[i]).is(":visible") == true) {
            id = inputs[i].id;
            text = inputs[i].value;
            dObject = new DataObject(id, text);

            list.unshift(dObject);
        }
    }

    return list;

}

function clearData(r) {
    clear(dataIdx);
}

function clearPage() {
    var inputs = $('input');
    var textareas = $('textarea');
    var childDdl = $("select[size=7]"); //select all dependent drop down lists so we can empty them

    for (var i = 0; i < inputs.length; i++) {
        inputs[i].value = "";
    }

    for (var i = 0; i < textareas.length; i++) {
        textareas[i].value = "";
    }

    for (var i = 0; i < childDdl.length; i++) {
        removeOptions(childDdl[i].id);
    }
    
}

function doNothing(r) {
    console.log("returned: " + r);
}
/* ************************************************************* */
/* ******************* Helper Functions ******************* */
/* ************************************************************* */
//  Invoke Service that goes through the task model to ensure all steps are complete.
function check_All_Steps() {
}
/****************** AUI ***********************/
// Adds aui object to aui index
function addAUI(aui, list) {
    //check for dupliactes
    for (var i = 0; i < list.length; i++) {
        if (aui.id == list[i].id) {
            update(aui, list[i])
            return false;
        }
    } //end for
    //no duplicates found, so add to list.
    list.unshift(aui);
}
//updates the contents of nAui (new AUI) to oAUI (old AUI)
function update(nAui, oAui) {
    oAui.totalTime += nAui.totalTime;
    oAui.yMouseV = nAui.yMouseV;
    oAui.yMouseA = nAui.yMouseA;
    oAui.dwellTime = nAui.dwellTime;
    oAui.nrVisitedItems = nAui.nrVisitedItems;
    oAui.uniqueItemCount = nAui.uniqueItemCount;
    oAui.selectionTime = nAui.selectionTime;
    // These must be recalculated!
    oAui.avgDwellTime = nAui.avgDwellTime;
    oAui.KLMRatio = nAui.KLMRatio;
    oAui.KLMDifference = nAui.KLMDifference;
}
function startIncrement() {
    interval = setInterval('incrDwellTime()', 1000);
}
function incrDwellTime() {
    aui.IncrDwellTime(1);
    console.log('Increased dwelltime: ' + aui.DwellTime() + ', Object id: ' + aui.getId());
}
// Adds an item to the list if it isn't there already.
function add(list, cur) {
    for (var i = 0; i < list.length; i++) {
        if (list[i] == cur)
            return;
    }
    list[list.length] = cur;
}
// Resets the list
function clear(list) {
    list = [];
}
function delayedStopTimer() {
    if (!mouseingOverOptions) {
        stopTimer();
    }
}

//Parameter working data lets us know if we are working from an element on screen or from the WorkingData.xml file
var gChild;
function fillDependent(parent, child, wsdl_url, WorkingData) {
    //Show the "loader" image for the dependent item.
    showDependentLoader(child);
    
    //check if wsdl is cached
    var wsdl_cache = cacheWsdl[wsdl_url];
    //don't let ajax cache this, we'll do it ourselves.
    $.ajaxSetup({
        cache: false
    });
    //get XML document (if given)
    if ((wsdl_url !== "undefined") && (wsdl_url !== "")) {
        gChild = child;
        $.ajax({
            type: "GET",
            url: wsdl_url,
            dataType: "xml",
            error: function() {
                alert('Error loading WSDL document');
            },
            success: function(xml) {
                //assign parent variable to global 'gChild' variable. NOT ELEGANT. Find a way to pass other parameters to a call back method
                //add to cache
                cacheWsdl[wsdl_url] = xml;
                processWsdl(xml, parent, child, wsdl_url, WorkingData, false);
            }
        });
    }
}

//caters for child elements with two parent elements
function fillDependents(parent1, parent2, child, wsdl_url, WorkingData) {
    //Show the "loader" image for the dependent item.
    showDependentLoader(child);

    //concat the parent names;
    var parent = parent1 + "," + parent2;
    var twoParents = true; // boolean denoting if more than 1 parent exists. i.e. true = more than one parent exists

    //check if wsdl is cached
    var wsdl_cache = cacheWsdl[wsdl_url];
    //don't let ajax cache this, we'll do it ourselves.
    $.ajaxSetup({
        cache: false
    });
    //get XML document (if given)
    if ((wsdl_url !== "undefined") && (wsdl_url !== "")) {
        gChild = child;
        $.ajax({
            type: "GET",
            url: wsdl_url,
            dataType: "xml",
            error: function() {
                alert('Error loading WSDL document');
            },
            success: function(xml) {
                //assign parent variable to global 'gChild' variable. NOT ELEGANT. Find a way to pass other parameters to a call back method
                //add to cache
                cacheWsdl[wsdl_url] = xml;
                processWsdl(xml, parent, child, wsdl_url, WorkingData, twoParents);
            }
        });
    }
}
function processWsdl(xml, parent, child, wsdl_url, WorkingData, twoParents) {
    var params = [];
    //find child element
    $(xml).find('s\\:element').each(function() {
        var name = $(this).attr("name");
        if (child == name) {
            $(this).find('s\\:element').each(function() {
                params.unshift(new Parameter($(this).attr("name"), $(this).attr("type")));
            });
        }
    });
    //test
    var cur;
    console.log("list of WSDL Parameters:" + params.length);
    //invoke method of child, with values from parent
    if (params.length !== 0) {
        invokeChildService(params, parent, child, wsdl_url, WorkingData);
    }
}
function invokeChildService(params, parent, child, wsdl_url, WorkingData) {
    //clean up url
    var url = cleanUp(wsdl_url);
    var pl = new SOAPClientParameters();
    var cur;

    //if we are NOT working from the WorkingData.xml then iterate through parameters and invoke child service
    if (WorkingData === false) {
        //Add paramaeters for service inocation
        for (var i = 0; i < params.length; i++) {
            cur = params[i];
            pl.add(cur.name, getValue(cur.name));
        }
        SOAPClient.invoke(url, child, pl, true, processChildService);
    } else {
    // if we ARE working from the WorkingData.xml, then fill all the paramenters with the parent elements value: this is because we
    // are working under the assumption that we will only use 1 value for the depenedents. i.e. we can't have a depenedent item use values from two
    // variables, it must be from one.
        for (var i = 0; i < params.length; i++) {
            cur = params[i];
            pl.add(cur.name, parent);
        }
        SOAPClient.invoke(url, child, pl, true, processChildService);
    }
}

//callback method to fill appropriate UI with data returned
function processChildService(r) {

    /*  If:                             How to test:
        ==                              ==============
        1. Single Value                 check if FALSE 'r.hasOwnProperty()', then find child element and plug value into it
        2. An Object with values        if TRUE 'r.hasOwnProperty()', then parse through all the items and plug the value's into the appropriate elements
        3. A list of single values      plug each single value as a new option in the select (lists are used by 'select' elements)
        4. A list of objects            plug the id of the object as the 'value' of the option and make some other identifier the 'text'
    
    */
    
    /* Poitns 3 or 4 */
    
    switch(typeof(r)){
        case "object":
            //Array therefore, fill select of some sort.
            if (r !== null && r.constructor === Array) {
                //find drop down list with gChild as id and populate it with array elements
                var divEls = document.getElementsByTagName("select");
                for (i = 0; i < divEls.length; i++) {
                    if (divEls[i].id === gChild) {
                        $('select#' + divEls[i].id).find('option').remove();

                        for (var p in r) {
                            if (r[p].UserID !== null || r[p].UserID !== "" || r[p].Surname !== "Customer not found") { //if user is found then add them to the drop down list
                                $('select#' + divEls[i].id).append('<option value="' + r[p].UserID + '">' + r[p].Surname + ", " + r[p].FirstName + " (" + r[p].UserID + ")" + '</option>');
                            } else { // if user is not found, then report it in the drop down list
                                $('select#' + divEls[i].id).append('<option value="' + r[p].UserID + '">' + r[p].Surname + "</option>");
                            }
                        }
                    }
                }
            } else {

                // for propery in r, find a UI element that matches (both name and type), and insert the value of the property into the element
                for (var p in r) {
                    //text input
                    if ($("#" + gChild).is(":input[type='text']")) {
                        //Clear the text from element
                        $("input#" + gChild).val("");
                        $("input#" + gChild).val(p);
                    }
                    //drop down list
                    if ($('div#' + gChild + ' #' + gChild).is('select')) {
                        //Clear options from element            
                        $("#" + gChild).append('<option value="' + r[p] + '">' + r[p] + '</option>');
                    }
                    // If the current element is a DIV then search inside the div to populate elements   
                    // 1. write logic so that if and only if the above 3 fail do we run the hasOwnProperty function below: Test and see side effects        
                    if (document.getElementById(gChild).tagName === "DIV") {
                        if (r.hasOwnProperty(p)) {
                            if ($("div#" + gChild + " input#" + p).is("input[type='text']")) {
                                $("div#" + gChild + " input#" + p).val(r[p]);
                            }
                        }
                    }
                }
            }
            break;        
    }
    
    //hide dependent loader
    hideDepLoader(gChild);
}
//removes ?WSDL from url
function cleanUp(wsdl_url) {
    return wsdl_url.substring(0, wsdl_url.indexOf('?'));
}
//returns for value of an item, regardless of type
function getValue(s) {
    if ($("#" + s).is("input[type='text']")) {
        return $("input#" + s).val();
    } else {
        if ($('select#' + s).is("select")) {
            return $('select#' + s).children("option:selected").val();              
        } //'elseif' for other input types goes here e.g. checkboxes, radio buttons
    }
}
//sets the current step varible: this function is called from the generated UI. However, this script is always called before the UI is generated so it should always work.
function setStep(n) {
    $('#step-counter').html("").append("Current Step: " + n);
}


//gets the value of the element n from the WorkingData.xml file

var curReliant;

function getReliant2(s) {
    _getReliant2(s);
    return curReliant;
}

function _getReliant2(s) {
    var pl = new SOAPClientParameters();
    pl.add("element", s);
    SOAPClient.invoke(url_reliants, "reliant", pl, false, getReliant2_CallBack);    
}

function getReliant2_CallBack(r) {
    curReliant = r;
}


/*************************** MY OBJECTS **********************************
Dependency scripts
Parent = Parent element
Child = Child Element
WSDL = url of WSDL
*/
//Array to store parameter names
var dParams = new Array();
function Parameter(name, type) {
    this.name = name;
    this.value = type;
}
//prototype function to get parameter name
Parameter.prototype.getName = function() {
    return this.name;
}
//prototype function to get parameter value
Parameter.prototype.getType = function() {
    return this.type;
}


//Search Result Objects
var SearchResult = function(fn, sn, id) {
    this.FirstName = fn;
    this.Surname = sn;
    this.UserID = id;
}
/***********************************************************/
